nanopyx.core.analysis.rcc
1# REF: based on https://github.com/jungmannlab/picasso/blob/d867f561ffeafce752f37968a20698556d04dafb/picasso/imageprocess.py 2 3import numpy as np 4import lmfit 5from tqdm import tqdm 6 7from .ccm import calculate_ccm_from_ref 8from .ccm_helper_functions import check_even_square, make_even_square 9 10# TODO: fix max_shift parameter 11 12def calculate_x_corr(im1: np.ndarray, im2: np.ndarray): 13 ccm = calculate_ccm_from_ref(np.array([im2]).astype(np.float32), im1.astype(np.float32))[0] 14 15 return np.array(ccm) 16 17 18def get_image_shift(im1: np.ndarray, im2: np.ndarray, box: int, max_shift: int = None): 19 """Computes the shift from im1 to ima2""" 20 21 if (np.sum(im1) == 0) or (np.sum(im2) == 0): 22 return 0, 0 23 24 # Compute image correlation 25 x_corr = calculate_x_corr(im1, im2) 26 27 # crop XCorr based on max_shift 28 w, h = im1.shape 29 if max_shift > 0: 30 x_border = int((w - max_shift) / 2) 31 y_border = int((h - max_shift) / 2) 32 if x_border > 0: 33 x_corr = x_corr[x_border:-x_border, :] 34 else: 35 x_border = 0 36 if y_border > 0: 37 x_corr = x_corr[:, y_border:-y_border] 38 else: 39 y_border = 0 40 else: 41 x_border = y_border = 0 42 43 # A quarter of the fit ROI 44 fit_box = int(box / 2) 45 46 # A coordinate grid for the fitting ROI 47 x, y = np.mgrid[-fit_box : fit_box + 1, -fit_box : fit_box + 1] 48 49 # Find the brightest pixel and cut out the fit ROI 50 x_max_xc, y_max_xc = np.unravel_index(x_corr.argmax(), x_corr.shape) 51 fit_roi = x_corr[ 52 x_max_xc - fit_box : y_max_xc + fit_box + 1, 53 y_max_xc - fit_box : y_max_xc + fit_box + 1, 54 ] 55 56 dimensions = fit_roi.shape 57 58 if 0 in dimensions or dimensions[0] != dimensions[1]: 59 xc, yc = 0, 0 60 else: 61 # The fit model 62 def flat_2d_gaussian(a, xc, yc, s, b): 63 A = a * np.exp(-0.5 * ((x - xc) ** 2 + (y - yc) ** 2) / s**2) + b 64 return A.flatten() 65 66 gaussian2d = lmfit.Model( 67 flat_2d_gaussian, name="2D Gaussian", independent_vars=[] 68 ) 69 70 # Set up initial parameters and fit 71 params = lmfit.Parameters() 72 params.add("a", value=fit_roi.max(), vary=True, min=0) 73 params.add("xc", value=0, vary=True) 74 params.add("yc", value=0, vary=True) 75 params.add("s", value=1, vary=True, min=0) 76 params.add("b", value=fit_roi.min(), vary=True, min=0) 77 tmp = fit_roi.flatten() 78 results = gaussian2d.fit(tmp, params) 79 80 # Get maximum coordinates and add offsets 81 xc = results.best_values["xc"] 82 yc = results.best_values["yc"] 83 xc += y_border + x_max_xc 84 yc += x_border + y_max_xc 85 86 xc -= np.floor(w / 2) 87 yc -= np.floor(h / 2) 88 89 return -xc, -yc 90 91 92def rcc(im_frames: np.ndarray, max_shift=None) -> tuple: 93 # REF: https://github.com/yinawang28/RCC 94 if not check_even_square(im_frames.astype(np.float32)): 95 im_frames = np.array(make_even_square(im_frames.astype(np.float32))) 96 n_frames = im_frames.shape[0] 97 shifts_x = np.zeros((n_frames, n_frames)) 98 shifts_y = np.zeros((n_frames, n_frames)) 99 n_pairs = int(n_frames * (n_frames - 1) / 2) 100 flag = 0 101 102 with tqdm( 103 total=n_pairs, desc="Correlating image pairs", unit="pairs" 104 ) as progress_bar: 105 for i in range(n_frames - 1): 106 for j in range(i + 1, n_frames): 107 progress_bar.update() 108 shifts_x[i, j], shifts_y[i, j] = get_image_shift( 109 im_frames[i], im_frames[j], 5, max_shift 110 ) 111 flag += 1 112 113 return minimize_shifts(shifts_x, shifts_y) 114 115 116def minimize_shifts(shifts_x, shifts_y, shifts_z=None): 117 n_channels = shifts_x.shape[0] 118 n_pairs = int(n_channels * (n_channels - 1) / 2) 119 n_dims = 2 if shifts_z is None else 3 120 rij = np.zeros((n_pairs, n_dims)) 121 A = np.zeros((n_pairs, n_channels - 1)) 122 flag = 0 123 for i in range(n_channels - 1): 124 for j in range(i + 1, n_channels): 125 rij[flag, 0] = shifts_x[i, j] 126 rij[flag, 1] = shifts_y[i, j] 127 if n_dims == 3: 128 rij[flag, 2] = shifts_z[i, j] 129 A[flag, i:j] = 1 130 flag += 1 131 Dj = np.dot(np.linalg.pinv(A), rij) 132 shift_x = np.insert(np.cumsum(Dj[:, 0]), 0, 0) 133 shift_y = np.insert(np.cumsum(Dj[:, 1]), 0, 0) 134 if n_dims == 2: 135 return shift_x, shift_y 136 else: 137 shift_z = np.insert(np.cumsum(Dj[:, 2]), 0, 0) 138 return shift_x, shift_y, shift_z
def
calculate_x_corr(im1: numpy.ndarray, im2: numpy.ndarray):
def
get_image_shift( im1: numpy.ndarray, im2: numpy.ndarray, box: int, max_shift: int = None):
19def get_image_shift(im1: np.ndarray, im2: np.ndarray, box: int, max_shift: int = None): 20 """Computes the shift from im1 to ima2""" 21 22 if (np.sum(im1) == 0) or (np.sum(im2) == 0): 23 return 0, 0 24 25 # Compute image correlation 26 x_corr = calculate_x_corr(im1, im2) 27 28 # crop XCorr based on max_shift 29 w, h = im1.shape 30 if max_shift > 0: 31 x_border = int((w - max_shift) / 2) 32 y_border = int((h - max_shift) / 2) 33 if x_border > 0: 34 x_corr = x_corr[x_border:-x_border, :] 35 else: 36 x_border = 0 37 if y_border > 0: 38 x_corr = x_corr[:, y_border:-y_border] 39 else: 40 y_border = 0 41 else: 42 x_border = y_border = 0 43 44 # A quarter of the fit ROI 45 fit_box = int(box / 2) 46 47 # A coordinate grid for the fitting ROI 48 x, y = np.mgrid[-fit_box : fit_box + 1, -fit_box : fit_box + 1] 49 50 # Find the brightest pixel and cut out the fit ROI 51 x_max_xc, y_max_xc = np.unravel_index(x_corr.argmax(), x_corr.shape) 52 fit_roi = x_corr[ 53 x_max_xc - fit_box : y_max_xc + fit_box + 1, 54 y_max_xc - fit_box : y_max_xc + fit_box + 1, 55 ] 56 57 dimensions = fit_roi.shape 58 59 if 0 in dimensions or dimensions[0] != dimensions[1]: 60 xc, yc = 0, 0 61 else: 62 # The fit model 63 def flat_2d_gaussian(a, xc, yc, s, b): 64 A = a * np.exp(-0.5 * ((x - xc) ** 2 + (y - yc) ** 2) / s**2) + b 65 return A.flatten() 66 67 gaussian2d = lmfit.Model( 68 flat_2d_gaussian, name="2D Gaussian", independent_vars=[] 69 ) 70 71 # Set up initial parameters and fit 72 params = lmfit.Parameters() 73 params.add("a", value=fit_roi.max(), vary=True, min=0) 74 params.add("xc", value=0, vary=True) 75 params.add("yc", value=0, vary=True) 76 params.add("s", value=1, vary=True, min=0) 77 params.add("b", value=fit_roi.min(), vary=True, min=0) 78 tmp = fit_roi.flatten() 79 results = gaussian2d.fit(tmp, params) 80 81 # Get maximum coordinates and add offsets 82 xc = results.best_values["xc"] 83 yc = results.best_values["yc"] 84 xc += y_border + x_max_xc 85 yc += x_border + y_max_xc 86 87 xc -= np.floor(w / 2) 88 yc -= np.floor(h / 2) 89 90 return -xc, -yc
Computes the shift from im1 to ima2
def
rcc(im_frames: numpy.ndarray, max_shift=None) -> tuple:
93def rcc(im_frames: np.ndarray, max_shift=None) -> tuple: 94 # REF: https://github.com/yinawang28/RCC 95 if not check_even_square(im_frames.astype(np.float32)): 96 im_frames = np.array(make_even_square(im_frames.astype(np.float32))) 97 n_frames = im_frames.shape[0] 98 shifts_x = np.zeros((n_frames, n_frames)) 99 shifts_y = np.zeros((n_frames, n_frames)) 100 n_pairs = int(n_frames * (n_frames - 1) / 2) 101 flag = 0 102 103 with tqdm( 104 total=n_pairs, desc="Correlating image pairs", unit="pairs" 105 ) as progress_bar: 106 for i in range(n_frames - 1): 107 for j in range(i + 1, n_frames): 108 progress_bar.update() 109 shifts_x[i, j], shifts_y[i, j] = get_image_shift( 110 im_frames[i], im_frames[j], 5, max_shift 111 ) 112 flag += 1 113 114 return minimize_shifts(shifts_x, shifts_y)
def
minimize_shifts(shifts_x, shifts_y, shifts_z=None):
117def minimize_shifts(shifts_x, shifts_y, shifts_z=None): 118 n_channels = shifts_x.shape[0] 119 n_pairs = int(n_channels * (n_channels - 1) / 2) 120 n_dims = 2 if shifts_z is None else 3 121 rij = np.zeros((n_pairs, n_dims)) 122 A = np.zeros((n_pairs, n_channels - 1)) 123 flag = 0 124 for i in range(n_channels - 1): 125 for j in range(i + 1, n_channels): 126 rij[flag, 0] = shifts_x[i, j] 127 rij[flag, 1] = shifts_y[i, j] 128 if n_dims == 3: 129 rij[flag, 2] = shifts_z[i, j] 130 A[flag, i:j] = 1 131 flag += 1 132 Dj = np.dot(np.linalg.pinv(A), rij) 133 shift_x = np.insert(np.cumsum(Dj[:, 0]), 0, 0) 134 shift_y = np.insert(np.cumsum(Dj[:, 1]), 0, 0) 135 if n_dims == 2: 136 return shift_x, shift_y 137 else: 138 shift_z = np.insert(np.cumsum(Dj[:, 2]), 0, 0) 139 return shift_x, shift_y, shift_z